package org.jiamandan.com.config;


import cn.hutool.extra.spring.SpringUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jiamandan.com.cache.CacheNames;
import org.jiamandan.com.service.IRechargeGunService;
import org.jiamandan.com.service.IRechargePileService;
import org.jiamandan.com.utils.RedisUtil;
import org.springframework.stereotype.Component;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Netty空闲事件处理器
 * 处理读超时、写超时、读写超时事件
 */
@Slf4j
@Component
public class NettyIdleEventHandler extends ChannelInboundHandlerAdapter {

    private static final NettyProperties nettyProperties;
    private static final RedisUtil redisUtil;
    private static final IRechargePileService rechargePileService;
    private static final IRechargeGunService rechargeGunService;

    // 记录每个channel的空闲次数
    private static final Map<String, Integer> idleCountMap = new ConcurrentHashMap<>();
    // 记录channel对应的设备号
    private static final Map<String, String> channelDeviceMap = new ConcurrentHashMap<>();

    static {
        nettyProperties = SpringUtil.getBean(NettyProperties.class);
        redisUtil = SpringUtil.getBean(RedisUtil.class);
        rechargePileService = SpringUtil.getBean(IRechargePileService.class);
        rechargeGunService = SpringUtil.getBean(IRechargeGunService.class);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        String channelId = ctx.channel().id().asShortText();
        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();

        log.info("Channel激活 - ID: {}, 远程地址: {}", channelId, remoteAddress);

        // 清空该channel的空闲计数
        idleCountMap.remove(channelId);

        super.channelActive(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String channelId = ctx.channel().id().asShortText();

        // 收到数据，重置空闲计数
        if (idleCountMap.containsKey(channelId)) {
            idleCountMap.put(channelId, 0);
            log.debug("重置Channel {} 的空闲计数", channelId);
        }

        super.channelRead(ctx, msg);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (!(evt instanceof IdleStateEvent)) {
            super.userEventTriggered(ctx, evt);
            return;
        }

        IdleStateEvent event = (IdleStateEvent) evt;
        String channelId = ctx.channel().id().asShortText();
        InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();

        // 获取配置的最大空闲次数
        int maxIdleCount = nettyProperties.getMaxIdleCount() != null ?
                          nettyProperties.getMaxIdleCount() : 3;

        // 处理不同类型的空闲事件
        if (event.state() == IdleState.READER_IDLE) {
            handleReaderIdle(ctx, channelId, remoteAddress, maxIdleCount);
        } else if (event.state() == IdleState.WRITER_IDLE) {
            handleWriterIdle(ctx, channelId, remoteAddress);
        } else if (event.state() == IdleState.ALL_IDLE) {
            handleAllIdle(ctx, channelId, remoteAddress, maxIdleCount);
        }

        super.userEventTriggered(ctx, evt);
    }

    /**
     * 处理读超时事件
     */
    private void handleReaderIdle(ChannelHandlerContext ctx, String channelId,
                                  InetSocketAddress remoteAddress, int maxIdleCount) {
        // 增加空闲计数
        int idleCount = idleCountMap.compute(channelId, (k, v) -> v == null ? 1 : v + 1);

        log.warn("读超时事件 - Channel: {}, 远程地址: {}, 空闲次数: {}/{}", channelId, remoteAddress, idleCount, maxIdleCount);

        // 获取设备号
        String deviceNo = getDeviceNoByChannelId(channelId);

        if (idleCount < maxIdleCount) {
            // 还未达到最大空闲次数，发送心跳检测
            sendHeartbeatCheck(channelId, deviceNo, "READER_IDLE", idleCount);

            // 可以选择发送ping消息到客户端
            // ctx.writeAndFlush(createPingMessage());
        } else {
            // 达到最大空闲次数，处理设备离线
            log.error("Channel {} 读超时次数达到上限，准备断开连接", channelId);

            // 处理设备离线
            if (StringUtils.isNotBlank(deviceNo)) {
                handleDeviceOffline(deviceNo, channelId);
            }

            // 清理资源
            cleanupChannel(channelId);

            // 关闭连接
            ctx.close();
        }
    }

    /**
     * 处理写超时事件
     */
    private void handleWriterIdle(ChannelHandlerContext ctx, String channelId,
                                  InetSocketAddress remoteAddress) {
        log.info("写超时事件 - Channel: {}, 远程地址: {}", channelId, remoteAddress);

        // 写超时通常意味着没有数据需要发送给客户端
        // 可以在这里发送心跳包保持连接
        String deviceNo = getDeviceNoByChannelId(channelId);
        sendHeartbeatCheck(channelId, deviceNo, "WRITER_IDLE", 0);
    }

    /**
     * 处理读写都超时事件
     */
    private void handleAllIdle(ChannelHandlerContext ctx, String channelId,
                               InetSocketAddress remoteAddress, int maxIdleCount) {
        // 增加空闲计数
        int idleCount = idleCountMap.compute(channelId, (k, v) -> v == null ? 1 : v + 1);

        log.warn("读写都超时事件 - Channel: {}, 远程地址: {}, 空闲次数: {}/{}",
                channelId, remoteAddress, idleCount, maxIdleCount);

        String deviceNo = getDeviceNoByChannelId(channelId);

        if (idleCount >= maxIdleCount) {
            // 读写都超时达到上限，断开连接
            log.error("Channel {} 读写都超时次数达到上限，断开连接", channelId);

            if (StringUtils.isNotBlank(deviceNo)) {
                handleDeviceOffline(deviceNo, channelId);
            }

            cleanupChannel(channelId);
            ctx.close();
        } else {
            sendHeartbeatCheck(channelId, deviceNo, "ALL_IDLE", idleCount);
        }
    }

    /**
     * 获取Channel对应的设备号
     */
    private String getDeviceNoByChannelId(String channelId) {
        // 先从本地缓存获取
        String deviceNo = channelDeviceMap.get(channelId);
        if (StringUtils.isNotBlank(deviceNo)) {
            return deviceNo;
        }

        // 从Redis获取
        Map<String, ChannelId> tcpChannelMap = (Map<String, ChannelId>) redisUtil.get(CacheNames.TCP_CHANNEL);
        if (tcpChannelMap != null) {
            for (Map.Entry<String, ChannelId> entry : tcpChannelMap.entrySet()) {
                if (entry.getValue() != null && channelId.equals(entry.getValue().asShortText())) {
                    deviceNo = entry.getKey();
                    channelDeviceMap.put(channelId, deviceNo);
                    return deviceNo;
                }
            }
        }

        return null;
    }

    /**
     * 发送心跳检测消息到MQ
     */
    private void sendHeartbeatCheck(String channelId, String deviceNo, String idleType, int idleCount) {
        try {
            String payload = String.format(
                "{\"channelId\":\"%s\",\"deviceNo\":\"%s\",\"idleType\":\"%s\",\"idleCount\":%d,\"timestamp\":%d}",
                channelId,
                deviceNo != null ? deviceNo : "",
                idleType,
                idleCount,
                System.currentTimeMillis()
            );
            //TODO 根据业务逻辑法发送不发送
            /*
            rocketMQTemplate.asyncSend(
                mqProperties.getOrderChargeProfitTopic() + ":" + BizEventTag.NETTY_HEART_BEAT.name(),
                payload,
                RocketmqSendCallbackBuilder.commonCallback()
            );*/

            log.debug("发送心跳检测到MQ - {}", payload);
        } catch (Exception e) {
            log.error("发送心跳检测消息失败: ", e);
        }
    }

    /**
     * 处理设备离线
     */
    private void handleDeviceOffline(String pileNo, String channelId) {
        log.error("设备离线处理 - 设备号: {}, Channel: {}", pileNo, channelId);
        //TODO 根据业务逻辑处理
/*
        try {
            // 更新充电桩状态
            RechargePile rechargePile = rechargePileService.getByPileNo(pileNo);
            if (rechargePile != null) {
                rechargePile.setLiveStatus(BizConstant.OnOffLine.OFFLINE);
                rechargePileService.updateById(rechargePile);
            }

            // 更新充电枪状态
            UpdateWrapper<RechargeGun> updateWrapper = Wrappers.<RechargeGun>update();
            updateWrapper.set("online_status", BizConstant.OnOffLine.OFFLINE);
            updateWrapper.eq("pile_no", pileNo);
            rechargeGunService.update(updateWrapper);

            // 发送离线事件到MQ（延迟30分钟）
            Message<String> message = MessageBuilder.withPayload(pileNo).build();
            rocketMQTemplate.asyncSend(
                mqProperties.getIssueChargeRetryTopic() + ":" + BizEventTag.DEVICE_OFFLINE.name(),
                message,
                RocketmqSendCallbackBuilder.commonCallback(),
                3000,
                16  // 延迟级别16对应30分钟
            );
*/
            log.info("设备 {} 已设置为离线状态", pileNo);
    }

    /**
     * 清理Channel相关资源
     */
    private void cleanupChannel(String channelId) {
        // 清理空闲计数
        idleCountMap.remove(channelId);

        // 清理设备映射
        String deviceNo = channelDeviceMap.remove(channelId);

        // 清理Redis缓存
        if (StringUtils.isNotBlank(deviceNo)) {
            cleanupRedisCache(channelId, deviceNo);
        }

        log.info("清理Channel资源完成 - Channel: {}, 设备号: {}", channelId, deviceNo);
    }

    /**
     * 清理Redis缓存
     */
    private void cleanupRedisCache(String channelId, String deviceNo) {
        try {
            // 清理TCP Channel缓存
            Map<String, ChannelId> tcpChannelMap = (Map<String, ChannelId>) redisUtil.get(CacheNames.TCP_CHANNEL);
            if (tcpChannelMap != null && tcpChannelMap.containsKey(deviceNo)) {
                tcpChannelMap.remove(deviceNo);
                redisUtil.set(CacheNames.TCP_CHANNEL, tcpChannelMap);
            }
        } catch (Exception e) {
            log.error("清理Redis缓存失败: ", e);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        String channelId = ctx.channel().id().asShortText();
        log.info("Channel失活 - ID: {}", channelId);
        // 清理资源
        cleanupChannel(channelId);
        super.channelInactive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        String channelId = ctx.channel().id().asShortText();
        log.error("Channel异常 - ID: {}, 错误: ", channelId, cause);

        // 清理资源
        cleanupChannel(channelId);

        // 关闭连接
        ctx.close();
    }
}